#include <stdio.h>
#include "dearch.h"

#define E_FILE_OPEN -2
#define E_OUT_OF_MEMORY -3
#define E_FILE_EMPTY -5
#define E_FILE_CONTENT -6

#define CHAR_SIZE (8 * sizeof (char))
#define BRACKET_SIZE 1
#define BIT_SIZE 1
#define BITE 1
#define NUM_BITES 1

#define LEFT_BRANCH 0

#define OPEN_BRACKET 1
#define CLOSE_BRACKET 0

unsigned char readElement (FILE * input, int size, unsigned char * cur_ch, char * mod) {
    unsigned char res = 0;

    if (*mod > size) {

        res = *cur_ch >> (*mod - size);
        res &= (1 << size) - 1;
    } else {
        res = *cur_ch << (size - *mod);
        res &= (1 << size) - 1;
        size -= *mod;

        if (fread (cur_ch, BITE, NUM_BITES, input) != 1) {
            return E_FILE_CONTENT;
        }
        (*mod) = CHAR_SIZE;
        res += (*cur_ch >> (*mod - size)) & ((1 << size) - 1);
    }

    *mod -= size;

    return res;
}

int readTree (FILE * input, tree ** root, unsigned char * cur_ch, char * mod) {
    unsigned char bracket = 0;
    tree * new_el = NULL;

    do {
        bracket = readElement (input, BRACKET_SIZE, cur_ch, mod);
        if (bracket == OPEN_BRACKET) {
            if (! (*root)->left) {
                if (createTreeElement(&new_el) != 0) {
                    return E_OUT_OF_MEMORY;
                }
                (*root)->left = new_el;
                new_el->data = readElement (input, CHAR_SIZE, cur_ch, mod);
                readTree (input, &((*root)->left), cur_ch, mod);
            } else {
                if (createTreeElement(&new_el) != 0) {
                    return E_OUT_OF_MEMORY;
                }
                (*root)->right = new_el;
                new_el->data = readElement (input, CHAR_SIZE, cur_ch, mod);
                readTree (input, &((*root)->right), cur_ch, mod);
            }
        }
    } while (bracket != CLOSE_BRACKET);

    return 0;
}

int createTree (FILE * input, tree ** root) {
    char mod = CHAR_SIZE;
    unsigned char cur_ch = 0;
    unsigned char bracket = 0;

    if (fread (&cur_ch, BITE, NUM_BITES, input) != 1) {
        return E_FILE_EMPTY;
    }

    bracket = readElement (input, BRACKET_SIZE, &cur_ch, &mod);

    if (bracket == OPEN_BRACKET) {
        if (createTreeElement(root) != 0) {
            return E_OUT_OF_MEMORY;
        }
        (*root)->data = readElement (input, CHAR_SIZE, &cur_ch, &mod);
    } else {
        return E_FILE_CONTENT;
    }

    readTree (input, root, &cur_ch, &mod);

    return 0;
}

int readBit (unsigned char cur_ch, char * mod) {
    unsigned char res = 0;

    res = cur_ch >> (*mod - BIT_SIZE);
    res &= (1 << BIT_SIZE) - 1;

    *mod -= BIT_SIZE;

    return res;
}

int readContent (FILE * input, FILE * output, tree * root) {
    int res = 0;
    char mod = CHAR_SIZE;
    char bit = 0;
    unsigned char first_ch = 0;
    unsigned char second_ch = 0;
    unsigned char third_ch = 0;
    tree * tmp = root;
    int i = 0;

    if (fread (&first_ch, BITE, NUM_BITES, input) != 1) {
        return E_FILE_CONTENT;
    }

    if (fread (&second_ch, BITE, NUM_BITES, input) != 1) {
        return E_FILE_CONTENT;
    }

    res = fread (&third_ch, BITE, NUM_BITES, input);

    while (res == 1) {
        if (mod > 0) {
            bit = readBit (first_ch, &mod);
            if (bit == LEFT_BRANCH) {
                if (tmp->left) {
                    tmp = tmp->left;
                } else {
                    fwrite (&(tmp->data), BITE, NUM_BITES, output);
                    tmp = root->left;
                }
            } else {
                if (tmp->right) {
                    tmp = tmp->right;
                } else {
                    fwrite (&(tmp->data), BITE, NUM_BITES, output);
                    tmp = root->right;
                }
            }
        } else {
            first_ch = second_ch;
            second_ch  = third_ch;
            res = fread (&third_ch, BITE, NUM_BITES, input);
            mod = CHAR_SIZE;
        }
    }

    for (i = 0; i < second_ch; i++) {
        bit = readBit (first_ch, &mod);
        if (bit == LEFT_BRANCH) {
            if (tmp->left) {
                tmp = tmp->left;
            } else {
                fwrite (&(tmp->data), BITE, NUM_BITES, output);
                tmp = root->left;
            }
        } else {
            if (tmp->right) {
                tmp = tmp->right;
            } else {
                fwrite (&(tmp->data), BITE, NUM_BITES, output);
                tmp = root->right;
            }
        }
    }
    fwrite (&(tmp->data), BITE, NUM_BITES, output);

    return 0;
}

int dearchivate (char * input_file, char * output_file) {
    FILE * input = fopen (input_file, "rb");
    FILE * output = NULL;
    tree * root = NULL;
    int error = 0;

    if (! input) { /* check file open*/
        return E_FILE_OPEN;
    }
    
    output = fopen (output_file, "wb");
    if (! output) { /* check file open*/
        return E_FILE_OPEN;
    }

    if ((error = createTree (input, &root)) != 0) {
        return error;
    }

    if ((error = readContent (input, output, root)) != 0) {
        return error;
    }

    clearTree (&root);

    fclose (input);
    fclose (output);

    return 0;
}
